package com.shiroha.user.service.impl;

import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.shiroha.user.mapper.FollowMapper;
import com.shiroha.user.pojo.Follow;
import com.shiroha.user.pojo.vo.FollowInfo;
import com.shiroha.user.pojo.vo.FollowResponse;
import com.shiroha.user.service.IFollowService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.shiroha.user.pojo.table.Tables.CACHED_USER;
import static com.shiroha.user.pojo.table.Tables.FOLLOW;

@Service
@Slf4j
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
    private final IFollowService self;

    @Autowired
    public FollowServiceImpl(@Lazy IFollowService self) {
        this.self = self;
    }

    /**
     * 查找用户的关注/粉丝列表（关注者或被关注者）
     *
     * @param userId 当前用户的ID
     * @param isFollowee 是否查找关注者（true: 关注者，false: 粉丝）
     * @return 用户的关注/粉丝列表
     */
    private List<FollowInfo> findFollowList(UUID userId, boolean isFollowee) {
        QueryWrapper queryWrapper = QueryChain.create()
                .select(
                        FOLLOW.ID.as("id"),
                        CACHED_USER.ID.as("user_id"),
                        CACHED_USER.USERNAME,
                        CACHED_USER.AVATAR
                )
                // 连接查询的目标用户（关注者或者被关注者）
                .leftJoin(CACHED_USER).on(isFollowee ? FOLLOW.FOLLOWEE_ID.eq(CACHED_USER.ID) : FOLLOW.FOLLOWER_ID.eq(CACHED_USER.ID))
                .where(isFollowee ? FOLLOW.FOLLOWER_ID.eq(userId) : FOLLOW.FOLLOWEE_ID.eq(userId));

        // 获取包含所有朋友ID的集合
        Set<UUID> friendsIds = getFriendIds(userId);

        // 获取到用户的关注/粉丝列表，并通过与朋友ID集合作比对确定是否是互关关系
        return this.listAs(queryWrapper, FollowInfo.class)
                .stream()
                .peek(followInfo -> followInfo.setMutualFollow(friendsIds.contains(followInfo.getUserId())))
                .toList();
    }

    /**
     * 新增关注
     *
     * @param followerId 关注者ID
     * @param followeeId 粉丝ID
     */
    @Override
    @Caching(
            evict = {
                    @CacheEvict(cacheNames = "followee", key = "#followerId"),
                    @CacheEvict(cacheNames = "follower", key = "#followeeId"),
                    @CacheEvict(cacheNames = "friend", key = "#followerId or #followeeId")
            }
    )
    public void saveFollow(UUID followerId, UUID followeeId) {
        Follow follow = Follow.create()
                // 关注用户为 follower
                .setFollowerId(followerId)
                // 发起关注用户为 followee
                .setFolloweeId(followeeId);
        this.save(follow);
    }

    /**
     * 查找用户的关注列表
     *
     * @param userId 当前用户的ID
     * @return FollowResponse 封装对象
     */
    @Override
    @Cacheable(cacheNames = "followee", key = "#userId.toString()")
    public FollowResponse findFolloweesByUserId(UUID userId) {
        return getFollowResponse(userId, findFollowList(userId, true));  // true 表示关注者
    }

    /**
     * 查找用户的粉丝列表
     *
     * @param userId 被关注的用户的ID
     * @return FollowResponse 封装对象
     */
    @Override
    @Cacheable(cacheNames = "follower", key = "#userId.toString()")
    public FollowResponse findFollowersByUserId(UUID userId) {
        return getFollowResponse(userId, findFollowList(userId, false));  // false 表示粉丝
    }

    /**
     * 查找用户的互关（即朋友关系）列表
     *
     * @param userId 目标用户id
     * @return FollowResponse 封装对象
     */
    @Override
    @Cacheable(cacheNames = "friend", key = "#userId.toString()")
    public FollowResponse findFriendsByUserId(UUID userId) {
        QueryWrapper queryWrapper = QueryChain.create()
                .select(
                        FOLLOW.as("f1").ID.as("id"),
                        CACHED_USER.ID.as("user_id"),
                        CACHED_USER.USERNAME,
                        CACHED_USER.AVATAR
                )
                .from(FOLLOW.as("f1"))
                // 自连接，找出该用户关注的用户（followee）
                .innerJoin(FOLLOW.as("f2"))
                .on(FOLLOW.as("f2").FOLLOWEE_ID.eq(FOLLOW.as("f1").FOLLOWER_ID)
                        .and(FOLLOW.as("f2").FOLLOWER_ID.eq(FOLLOW.as("f1").FOLLOWEE_ID)))  // 互关关系
                // 连接 cached_user 表，以获取被关注者的信息
                .innerJoin(CACHED_USER)
                .on(FOLLOW.as("f2").FOLLOWER_ID.eq(CACHED_USER.ID))
                .where(FOLLOW.as("f1").FOLLOWER_ID.eq(userId));
        List<FollowInfo> followInfos = this.listAs(queryWrapper, FollowInfo.class)
                .stream()
                .peek(followInfo -> followInfo.setMutualFollow(true))
                .toList();
        return getFollowResponse(userId, followInfos);
    }

    /**
     * 取消关注
     *
     * @param followerId 关注者ID
     * @param followeeId 被关注者ID
     * @return 取关成功返回 True
     */
    @Override
    public Boolean removeFollow(UUID followerId, UUID followeeId) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(FOLLOW.FOLLOWER_ID.eq(followerId))
                .and(FOLLOW.FOLLOWER_ID.eq(followeeId));
        return this.remove(queryWrapper);
    }

    /**
     * 检查两个用户是否是双向关注关系
     *
     * @param userId1 用户一ID
     * @param userId2 用户二ID
     * @return 是朋友则返回 True
     */
    @Override
    public Boolean isFriend(UUID userId1, UUID userId2) {
        // 检查用户 1 的朋友列表是否包含用户 2
        return this.getFriendIds(userId1).contains(userId2);
    }

    private FollowResponse getFollowResponse(UUID userId, List<FollowInfo> followInfos) {
        return FollowResponse.create()
                .setUserId(userId.toString())
                .setFollowInfos(followInfos)
                .setCount(followInfos.size());
    }

    /**
     * 获取用户的朋友列表并收集到一个集合中
     * @param userId 用户ID
     * @return UUID集合
     */
    private Set<UUID> getFriendIds(UUID userId) {
        // 获取用户的互关列表
        List<FollowInfo> friends = self.findFriendsByUserId(userId).getFollowInfos();
        // 将互关用户ID收集到一个集合中，以便快速查找
        return friends.stream()
                .map(FollowInfo::getUserId)
                .collect(Collectors.toSet());
    }
}
